﻿using UnityEngine;
using UnityEditor;
using UnityEngine.U2D;
using UnityEditor.U2D;
using System.IO;
using System.Collections.Generic;
using UnityEditor.Animations;

namespace NRatel.TextureUnpacker
{
    /* 
     * 命名规则：
     * 路径字符串 —— dir 表示不含文件的路径串，file 表示含文件的路径串。二者前面加os前缀表示系统路径
     * 
     * 2020/2/7
     * 放弃使用 Unity Sprite Atlas 图集，改为使用 Multi-Sprite 创建动画
     * （Unity Inspector 面板，Texture Type 选择 "Sprite (2D and UI)"）。
     * 和使用图集相比，由于必须按动画顺序罗列好，导致动画所使用的 png 文件资源较大
     * 使用图集的话，动画 .anim 文件中，找不到精灵的有效ID，都是0。暂时未找到有效解决方法。
     * 
     * 这里使用了两个插件：PList2Sprite 和 TextureUnpacker，都是国人写的。
     * 两个插件里各有一套解析 .plist (.xml) 文件的逻辑，这里不作整合了。
     * 
     * 使用方法：
     * 1. 打开 Assets/Editor/PackCmd.bat，设置好你的 TexturePacker 路径。
     * （PATH_TEXTURE_PACKER）其它参数不用管。
     * 
     * 2. 将待处理的资源，放到某个资源的 src/ 路径下，
     * 如：Assets/Resources/SrcAtlas/Example/src，其中包含一个 png 和原始 plist 文件
     * 
     * 3. 右键 plist 文件 ---> MyTools ---> Build Multi Sprite By plist （执行 Start() 函数）
     * 大致步骤：
     * (1) 将原始图集拆成碎图（TextureUnpacker 插件），
     * (2) 将碎图重新拼成 Multi-Sprite（PList2Sprite 插件），
     * 把 Multi-Sprite 文件复制一份到 dst/ 路径，
     * 如：Assets/Resources/SrcAtlas/Example/dst
     * (3) 在 dst/ 路径下，生成动画所需的 .anim 和 .controller 文件。
     * 
     * 4. 若上述步骤都能顺利执行，在 dst/ 路径下，可以右键 png 文件 
     * ---> MyTools ---> Create Test Anim Obj by Multi-Sprite
     * 创建一个带动画组件的 GameObject，测试生成的动画效果。
    */
    public class EditorMain
    {
        // 将磁盘路径转为 Unity 资源路径
        private static string ToAssetPath(string osPath)
        {
            if (osPath.IndexOf(":") == -1)
            {
                return "";
            }
            int startIdx = osPath.IndexOf("Assets");
            if (startIdx == -1)
            {
                return "";
            }

            // 返回 "Assets/..." 后面的子串
            return osPath.Substring(startIdx);
        }

        // 将 Unity 资源路径转为系统路径 
        private static string ToOSPath(string assetPath)
        {
            // 必须以 "Assets" 开头
            if (assetPath.Length < "Assets".Length)
            {
                return "";
            }
            if (assetPath.Substring(0, "Assets".Length) != "Assets")
            {
                return "";
            }

            // 提取 "Assets/..." 路径的上级目录
            string headPath = Path.GetDirectoryName(Application.dataPath).Replace("\\", "/");
            return headPath + "/" + assetPath;
        }

        // 根据选择的 Multi-Sprite，创建一个 GameObject，用来测试动画
        [MenuItem("Assets/MyTools/Create Test Anim Obj by Multi-Sprite", false, 1)]
        public static void CreateTestAnimObj()
        {
            string selectionPath = AssetDatabase.GetAssetPath(Selection.activeObject);
            if (!selectionPath.EndsWith(".png"))
            {
                EditorUtility.DisplayDialog("Error", "Please select a png file!", "OK", "");
                return;
            }

            // 加载 Multi-Sprite，注意，第一个元素是 Texture2D，第二个开始才是 Sprite
            Object[] spriteObjs = AssetDatabase.LoadAllAssetsAtPath(selectionPath);
            if (spriteObjs.Length <= 1)
            {
                return;
            }

            string fileAnimCtrler = selectionPath.Replace(".png", ".controller");
            AnimatorController runtimeAnimCtrler = AssetDatabase.LoadAssetAtPath<AnimatorController>(fileAnimCtrler);
            if (!runtimeAnimCtrler)
            {
                return;
            }


            GameObject objAnim = new GameObject();
            objAnim.name = Path.GetFileNameWithoutExtension(selectionPath);
            objAnim.transform.localScale = new Vector3(5.0f, 5.0f, 0.0f);

            // 挂载 SpriteRenderer 组件
            SpriteRenderer spriteRender = objAnim.AddComponent<SpriteRenderer>();
            spriteRender.sprite = (Sprite)spriteObjs[1];

            Animator animator = objAnim.AddComponent<Animator>();
            animator.runtimeAnimatorController = runtimeAnimCtrler;
        }


        // 切割成碎图
        private static bool SplitTexture(App app)
        {
            string strPlistPath = app.PlistFilePath;

            string plistFilePath = strPlistPath;
            string pngFilePath = strPlistPath.Replace("plist", "png");

            // Function LoadFiles()
            Loader loader = null;
            Plist plist = null;
            Texture2D bigTexture = null;
            try
            {
                loader = Loader.LookingForLoader(plistFilePath);
                if (loader != null)
                {
                    plist = loader.LoadPlist(plistFilePath);
                    bigTexture = loader.LoadTexture(pngFilePath, plist.metadata);
                    Debug.Log("名称: " + plist.metadata.textureFileName + "\n类型: format_" + plist.metadata.format + "\n大小: " + plist.metadata.size.width + "*" + plist.metadata.size.height);
                }
                else
                {
                    Debug.Log("无法识别的plist类型!!!\n请联系作者");
                    return false;
                }
            }
            catch
            {
                Debug.Log("出错了!!!\n请联系作者\n↓");
                return false;
            }

            //Function RegisterEvents()
            Core core = new Core(app);

            //Function Unpack()
            int total = plist.frames.Count;
            int count = 0;

            int frameSize = plist.frames.Count;
            for (int iFrame = 0; iFrame < frameSize; ++iFrame)
            {
                try
                {
                    core.Restore(bigTexture, plist.frames[iFrame]);

                    // 这种方式虽然打图集省资源，但是做出来的动画就不行了，会出现抖动
                    //core.JustSplit(bigTexture, plist.frames[iFrame]);

                    count += 1;
                    Debug.Log("进度：" + count + "/" + total + (count >= total ? "\n已完成！" : ""));
                }
                catch
                {
                    Debug.Log("出错了!!!\n请联系作者\n↓");
                    return false;
                }
                //yield return null;
            }//end for: iFrame

            // 碎图保存完毕，一定要刷新资源。否则后续操作无法进行。
            AssetDatabase.Refresh();
            return true;
        }


        private static void CreateAnim_By_MultiSprite()
        {
            string selectionPath = AssetDatabase.GetAssetPath(Selection.activeObject);
            if (!selectionPath.EndsWith(".png"))
            {
                EditorUtility.DisplayDialog("Error", "Please select a png file!", "OK", "");
                return;
            }

            int name_beg_idx = selectionPath.LastIndexOf("/");
            int name_end_idx = selectionPath.IndexOf(".png");
            string multiSpriteName = selectionPath.Substring(name_beg_idx + 1, name_end_idx - name_beg_idx - 1);
            string plistRootPath = selectionPath.Substring(0, name_beg_idx);


            selectionPath = selectionPath.Replace("\\", "/");
            int beg_idx = selectionPath.IndexOf("Assets");
            string multiSpritePath = selectionPath.Substring(beg_idx);

            List<Sprite> listSprite = new List<Sprite>();
            Object[] objs = AssetDatabase.LoadAllAssetsAtPath(multiSpritePath);
            if (objs.Length < 0)
            {
                return;
            }

            for (int i = 0; i < objs.Length; ++i)
            {
                if (objs[i].GetType() == typeof(Texture2D))
                {
                    //Texture2D t2d = (Texture2D)objs[i];
                    //Sprite s = Sprite.Create(t2d, new Rect(0, 0, t2d.width, t2d.height), Vector2.zero);
                    //listSprite.Add(s);
                    continue;
                }
                else
                {
                    listSprite.Add((Sprite)objs[i]);
                }
            }


            int spriteSize = listSprite.Count;

            //动画长度是按秒为单位，1/10就表示1秒切10张图片，根据项目的情况可以自己调节
            float frameTime = 1.0f / 10.0f;
            // 创建关键帧
            ObjectReferenceKeyframe[] keyFrames = new ObjectReferenceKeyframe[spriteSize];
            //逐帧插入动画
            for (int i = 0; i < spriteSize; ++i)
            {
                string num_str = i.ToString().PadLeft(4, '0');
                string spriteName = multiSpriteName + "_" + num_str;
                Sprite sprite = listSprite[i];

                keyFrames[i] = new ObjectReferenceKeyframe();
                keyFrames[i].time = frameTime * i;
                keyFrames[i].value = sprite;
            }

            AnimationClip clip = new AnimationClip();
            clip.frameRate = 30;//动画帧率，30比较合适
            clip.name = multiSpriteName;

            EditorCurveBinding curveBinding = new EditorCurveBinding();
            curveBinding.type = typeof(SpriteRenderer);
            curveBinding.path = "";
            curveBinding.propertyName = "m_Sprite";//这名字不能随便起

            // 设置动画循环播放
            UnityEditor.AnimationClipSettings clipSetting = AnimationUtility.GetAnimationClipSettings(clip);
            clipSetting.loopTime = true;
            AnimationUtility.SetAnimationClipSettings(clip, clipSetting);
            AnimationUtility.SetObjectReferenceCurve(clip, curveBinding, keyFrames);

            // 根据动画片段，创建动画控制器
            var animatorController = AnimatorController.CreateAnimatorControllerAtPath(plistRootPath + "/" + multiSpriteName + ".controller");
            AnimatorControllerLayer layer = animatorController.layers[0];
            AnimatorStateMachine sm = layer.stateMachine;

            AnimatorState state = sm.AddState("normal");
            state.motion = clip;
            sm.defaultState = state;

            // 保存
            AssetDatabase.CreateAsset(clip, plistRootPath + "/" + multiSpriteName + ".anim");
            AssetDatabase.SaveAssets();
        }


        [MenuItem("Assets/MyTools/Build Multi Sprite By plist", false, 0)]
        public static void Start()
        {
            string selectionPath = AssetDatabase.GetAssetPath(Selection.activeObject);
            if (!selectionPath.EndsWith(".plist"))
            {
                EditorUtility.DisplayDialog("Error", "Please select a plist file!", "OK", "");
                return;
            }

            // App 只是一个带路径的空壳，因为 app.Save() 方法需要用路径
            //strPlistPath : "Assets/Resources/anim-cannon-fire/anim-cannon-fire.plist";
            string fileSrcPlist = selectionPath.Replace("\\", "/");
            App app = new App(fileSrcPlist);
            // 碎图路径，参照 Core.cs
            string dirTexture = app.GetSaveDir().Replace("\\", "/") + "/RestoredPngs";

            int name_beg_idx = fileSrcPlist.LastIndexOf("/");
            int name_end_idx = fileSrcPlist.IndexOf(".plist");
            string plistName = fileSrcPlist.Substring(name_beg_idx + 1, name_end_idx - name_beg_idx - 1);
            string dirSrcPlist = fileSrcPlist.Substring(0, name_beg_idx);


            // 切割成碎图
            bool ret = false;
            ret = SplitTexture(app);
            if (!ret)
            {
                return;
            }

            // 执行Texture Packer 的 bat 批处理，将碎图重新按照 Multi-Sprite 的方式打图集
            // 会在碎图目录下，生成一份新的 plist 文件，名称和原始路径下的 plist 文件名相同
            string cmdArgs = "";
            cmdArgs += ToOSPath(dirTexture) + " ";
            cmdArgs += plistName + " ";
            cmdArgs += ToOSPath(dirTexture);

            using (System.Diagnostics.Process packCmdPro = new System.Diagnostics.Process())
            {
                packCmdPro.StartInfo.FileName = ToOSPath("Assets/Editor/PackCmd.bat");
                packCmdPro.StartInfo.Arguments = cmdArgs;
                packCmdPro.StartInfo.UseShellExecute = true;
                packCmdPro.StartInfo.CreateNoWindow = true;
                packCmdPro.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
                packCmdPro.Start();
                packCmdPro.WaitForExit();
            }
            AssetDatabase.Refresh();

            // 检查碎图路径下，是否生成了plist文件。
            // 若有，将焦点设置在该文件上，然后执行下一个插件的步骤
            // 注意，Directory.Exists() 需要传递参数是磁盘路径，而不是 Unity 资源路径
            //string pathHead = Path.GetDirectoryName(Application.dataPath).Replace("\\", "/");
            string fileNewPlist = dirTexture + "/" + plistName + ".plist";
            Object objNewPlist = AssetDatabase.LoadAssetAtPath<Object>(fileNewPlist);
            if (!objNewPlist)
            {
                return;
            }
            // 在碎图路径下，执行生成 Multi-Sprite 插件
            Selection.activeObject = objNewPlist;
            SpriteSheetConvert.ConvertSprite();


            // 复制生成的 Multi-Sprite 的 meta 文件到dst路径
            string osfileNewPlist = ToOSPath(fileNewPlist);
            if (!File.Exists(osfileNewPlist))
            {
                return;
            }
            string osfileNewPng = osfileNewPlist.Replace(".plist", ".png");
            string osfileNewPngMeta = osfileNewPlist.Replace(".plist", ".png.meta");
            string osfileDstPlist = Path.GetDirectoryName(ToOSPath(dirSrcPlist)) + "/dst";
            string osfileDstPng = osfileDstPlist + "/" + plistName + ".png";
            string osfileDstPngMeta = osfileDstPlist + "/" + plistName + ".png.meta";
            if (!Directory.Exists(osfileDstPlist))
            {
                Directory.CreateDirectory(osfileDstPlist);
            }
            File.Copy(osfileNewPng, osfileDstPng, true);
            File.Copy(osfileNewPngMeta, osfileDstPngMeta, true);
            AssetDatabase.Refresh();


            // 根据 Multi-Sprite 创建动画
            string fileDstPng = ToAssetPath(osfileDstPng);
            Object objDstPng = AssetDatabase.LoadAssetAtPath<Object>(fileDstPng);
            if (!objDstPng)
            {
                return;
            }
            Selection.activeObject = objDstPng;
            CreateAnim_By_MultiSprite();

            // 创建图集
            //AutoSetAtlasContents(plistRootPath, texturePath, plistName);

            //模拟打包（现在用代码会导致编辑器崩溃，还是手动点 Pack Preview 按钮吧）
            //ClickPackAtlas(plistRootPath, plistName);

            // 根据图集创建动画
            //CreateAnim(plistRootPath, plistName);
        }


        //////////////////////////////////////////////////  暂时用不上的逻辑  //////////////////////////////////////////////////////////////
        /*
         * 由于图集打包生效，必须通过手动点击 Inspector 面板上的 "Pack Preview" 完成
         * 故生成动画和生成图集不是放在一个操作完成，
         * 而是：生成图集->"Pack Preview"->重新点击plist文件创建动画
         */

        // 创建图集
        private static void AutoSetAtlasContents(string _atlasPath, string _texturePath, string atlasName)
        {
            SpriteAtlas atlas = new SpriteAtlas();
            // 设置参数 可根据项目具体情况进行设置
            SpriteAtlasPackingSettings packSetting = new SpriteAtlasPackingSettings()
            {
                blockOffset = 1,
                enableRotation = false,
                enableTightPacking = false,
                padding = 2,
            };
            atlas.SetPackingSettings(packSetting);

            SpriteAtlasTextureSettings textureSetting = new SpriteAtlasTextureSettings()
            {
                readable = false,
                generateMipMaps = false,
                sRGB = true,
                filterMode = FilterMode.Bilinear,
            };
            atlas.SetTextureSettings(textureSetting);

            TextureImporterPlatformSettings platformSetting = new TextureImporterPlatformSettings()
            {
                maxTextureSize = 2048,
                format = TextureImporterFormat.Automatic,
                crunchedCompression = true,
                textureCompression = TextureImporterCompression.Compressed,
                compressionQuality = 50,
            };
            atlas.SetPlatformSettings(platformSetting);

            AssetDatabase.CreateAsset(atlas, _atlasPath + "/" + atlasName + ".spriteatlas");

            // 方式1、添加文件
            DirectoryInfo dir = new DirectoryInfo(_texturePath);
            // 这里我使用的是png图片，已经生成Sprite精灵了
            FileInfo[] files = dir.GetFiles("*.png");
            List<Sprite> listSprite = new List<Sprite>();
            foreach (FileInfo file in files)
            {
                string filePath = _texturePath + "/" + file.Name;
                TextureImporter texture = AssetImporter.GetAtPath(filePath) as TextureImporter;
                if (texture != null)
                {
                    texture.textureType = TextureImporterType.Sprite;
                    AssetDatabase.ImportAsset(filePath, ImportAssetOptions.ForceUpdate);
                    AssetDatabase.Refresh();

                    Sprite sprite = AssetDatabase.LoadAssetAtPath<Sprite>(filePath);
                    listSprite.Add(sprite);
                }
            }
            atlas.Add(listSprite.ToArray());

            // 方式2、添加文件夹(这种方式和方式1只需要一种即可)
            //Object obj = AssetDatabase.LoadAssetAtPath(_texturePath, typeof(Object));
            //atlas.Add(new[] { obj });

            AssetDatabase.SaveAssets();

            AssetDatabase.RemoveUnusedAssetBundleNames();
            //SpriteAtlas.DestroyImmediate(atlas);
            AssetDatabase.Refresh();

            // 在这直接打包，编辑器会崩溃
            //System.Threading.Thread.Sleep(5000);
            //SpriteAtlasUtility.PackAtlases(new SpriteAtlas[] { atlas }, EditorUserBuildSettings.activeBuildTarget);
            //AssetDatabase.Refresh();
        }


        public static void ClickPackAtlas()
        {
            string selectionPath = AssetDatabase.GetAssetPath(Selection.activeObject);
            if (!selectionPath.EndsWith(".plist"))
            {
                EditorUtility.DisplayDialog("Error", "Please select a plist file!", "OK", "");
                return;
            }

            //strPlistPath : "Assets/Resources/anim-cannon-fire/anim-cannon-fire.plist";
            string strPlistPath = selectionPath.Replace("\\", "/");

            //strPlistPath : anim-cannon-fire
            //plistRootPath : Assets/Resources/anim-cannon-fire
            int name_beg_idx = strPlistPath.LastIndexOf("/");
            int name_end_idx = strPlistPath.IndexOf(".plist");
            string plistName = strPlistPath.Substring(name_beg_idx + 1, name_end_idx - name_beg_idx - 1);
            string plistRootPath = strPlistPath.Substring(0, name_beg_idx);


            string _atlasPath = plistRootPath;
            string atlasName = plistName;

            string atlasFullPath = _atlasPath + "/" + atlasName + ".spriteatlas";
            SpriteAtlas atlas = AssetDatabase.LoadAssetAtPath<SpriteAtlas>(atlasFullPath);

            SpriteAtlasUtility.PackAtlases(new SpriteAtlas[] { atlas }, EditorUserBuildSettings.activeBuildTarget);
        }


        //根据图集创建动画：atlasPath 只包含路径，atlasName 不包含后缀名
        //[MenuItem("Assets/MyTools/Create Anim By Plist")]
        public static void CreateAnimByPlist()
        {
            string selectionPath = AssetDatabase.GetAssetPath(Selection.activeObject);
            if (!selectionPath.EndsWith(".plist"))
            {
                EditorUtility.DisplayDialog("Error", "Please select a plist file!", "OK", "");
                return;
            }

            //strPlistPath : "Assets/Resources/anim-cannon-fire/anim-cannon-fire.plist";
            string strPlistPath = selectionPath.Replace("\\", "/");

            //strPlistPath : anim-cannon-fire
            //plistRootPath : Assets/Resources/anim-cannon-fire
            int name_beg_idx = strPlistPath.LastIndexOf("/");
            int name_end_idx = strPlistPath.IndexOf(".plist");
            string plistName = strPlistPath.Substring(name_beg_idx + 1, name_end_idx - name_beg_idx - 1);
            string plistRootPath = strPlistPath.Substring(0, name_beg_idx);


            string _atlasPath = plistRootPath;
            string atlasName = plistName;

            string atlasFullPath = _atlasPath + "/" + atlasName + ".spriteatlas";
            //string atlasFullPath = atlasName + ".spriteatlas";
            SpriteAtlas atlas = AssetDatabase.LoadAssetAtPath<SpriteAtlas>(atlasFullPath);

            int spriteSize = atlas.spriteCount;
            if (spriteSize <= 0)
            {
                Debug.Log("图集中未包含有效的精灵!!!\n请联系作者\n↓");
                return;
            }
            else if (spriteSize > 10000)
            {
                Debug.Log("图集大小超过10000!!!\n请联系作者\n↓");
                return;
            }

            //动画长度是按秒为单位，1/10就表示1秒切10张图片，根据项目的情况可以自己调节
            float frameTime = 1.0f / 10.0f;
            // 创建关键帧
            ObjectReferenceKeyframe[] keyFrames = new ObjectReferenceKeyframe[spriteSize];
            //逐帧插入动画
            for (int i = 0; i < spriteSize; ++i)
            {
                string num_str = i.ToString().PadLeft(4, '0');
                string spriteName = atlasName + "_" + num_str;
                Sprite sprite = atlas.GetSprite(spriteName);

                keyFrames[i] = new ObjectReferenceKeyframe();
                keyFrames[i].time = frameTime * i;
                keyFrames[i].value = sprite;
            }

            AnimationClip clip = new AnimationClip();
            clip.frameRate = 30;//动画帧率，30比较合适
            clip.name = plistName;

            EditorCurveBinding curveBinding = new EditorCurveBinding();
            curveBinding.type = typeof(SpriteRenderer);
            curveBinding.path = "";
            curveBinding.propertyName = "m_Sprite";//这名字不能随便起
            AnimationUtility.SetObjectReferenceCurve(clip, curveBinding, keyFrames);

            // 设置动画循环播放
            UnityEditor.AnimationClipSettings clipSetting = AnimationUtility.GetAnimationClipSettings(clip);
            clipSetting.loopTime = true;
            AnimationUtility.SetAnimationClipSettings(clip, clipSetting);

            // 根据动画片段，创建动画控制器
            var animatorController = AnimatorController.CreateAnimatorControllerAtPath(plistRootPath + "/" + plistName + ".controller");
            AnimatorControllerLayer layer = animatorController.layers[0];
            AnimatorStateMachine sm = layer.stateMachine;

            AnimatorState state = sm.AddState("normal");
            state.motion = clip;
            sm.defaultState = state;

            // 保存
            AssetDatabase.CreateAsset(clip, _atlasPath + "/" + atlasName + ".anim");
            AssetDatabase.SaveAssets();
        }

    }//end class EditorMain
}